<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <title>小球运动模拟器</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            touch-action: manipulation;
        }
        body {
            font-family: Arial, sans-serif;
            height: 100vh;
            width: 100vw;
            overflow: hidden;
            background-color: #f0f0f0;
        }
        #canvas {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            z-index: 1;
            cursor: pointer;
        }
        .controls {
            position: fixed;
            bottom: 20px;
            right: 20px;
            background-color: rgba(255, 255, 255, 0.9);
            padding: 12px;
            border-radius: 10px;
            box-shadow: 0 4px 10px rgba(0,0,0,0.2);
            z-index: 10;
            display: flex;
            flex-direction: column;
            gap: 8px;
            width: 300px;
            max-width: 90vw;
            resize: none;
            cursor: move;
        }
        .control-group {
            display: flex;
            align-items: center;
            justify-content: space-between;
            flex-wrap: nowrap;
            gap: 8px;
        }
        label {
            font-size: 14px;
            white-space: nowrap;
            flex-shrink: 0;
        }
        button {
            padding: 8px 15px;
            background-color: #4CAF50;
            color: white;
            border: none;
            border-radius: 20px;
            cursor: pointer;
            font-size: 14px;
            flex: 1;
            min-width: 0;
        }
        button:hover {
            background-color: #45a049;
        }
        .info-panel {
            position: fixed;
            top: 10px;
            left: 10px;
            background-color: rgba(255, 255, 255, 0.9);
            padding: 8px 12px;
            border-radius: 20px;
            box-shadow: 0 2px 5px rgba(0,0,0,0.1);
            z-index: 10;
            font-size: 14px;
            user-select: none;
            max-width: calc(100% - 20px);
        }
        input[type="range"] {
            width: 100px;
            flex-shrink: 1;
            min-width: 0;
        }
        input[type="checkbox"] {
            transform: scale(1.2);
            margin-left: 5px;
            flex-shrink: 0;
        }
        .value-display {
            min-width: 30px;
            text-align: center;
            font-size: 14px;
            flex-shrink: 0;
        }
        .buttons-group {
            display: flex;
            gap: 8px;
            margin-top: 5px;
        }

        @media (max-width: 600px) {
            .controls {
                width: 280px;
                padding: 10px;
                bottom: 10px;
                right: 10px;
            }
            .control-group {
                flex-wrap: wrap;
            }
            input[type="range"] {
                width: 80px;
            }
            label {
                font-size: 13px;
            }
        }
    </style>
</head>
<body>
    <div class="info-panel" id="infoPanel">小球数量: <span id="ballCount">0</span></div>
    
    <div class="controls" id="controls">
        <div class="control-group">
            <label for="collisionToggle">碰撞开关</label>
            <input type="checkbox" id="collisionToggle" checked>
        </div>
        <div class="control-group">
            <label for="shrinkToggle">碰撞缩小</label>
            <input type="checkbox" id="shrinkToggle" checked>
        </div>
        <div class="control-group">
            <label for="autoSpawnToggle">自动生成</label>
            <input type="checkbox" id="autoSpawnToggle">
        </div>
        <div class="control-group">
            <label for="ballSize">小球大小</label>
            <input type="range" id="ballSize" min="10" max="50" value="30">
            <span class="value-display" id="ballSizeValue">30</span>
        </div>
        <div class="control-group">
            <label for="shrinkFactor">缩小比例(%)</label>
            <input type="range" id="shrinkFactor" min="1" max="30" value="10">
            <span class="value-display" id="shrinkFactorValue">10</span>
        </div>
        <div class="buttons-group">
            <button id="clearBtn">清除所有</button>
            <button id="addBallBtn">添加小球</button>
        </div>
    </div>
    
    <canvas id="canvas"></canvas>
    
    <script>
        // 获取DOM元素
        const canvas = document.getElementById('canvas');
        const ctx = canvas.getContext('2d');
        const collisionToggle = document.getElementById('collisionToggle');
        const shrinkToggle = document.getElementById('shrinkToggle');
        const autoSpawnToggle = document.getElementById('autoSpawnToggle');
        const ballSizeSlider = document.getElementById('ballSize');
        const ballSizeValue = document.getElementById('ballSizeValue');
        const shrinkFactorSlider = document.getElementById('shrinkFactor');
        const shrinkFactorValue = document.getElementById('shrinkFactorValue');
        const clearBtn = document.getElementById('clearBtn');
        const addBallBtn = document.getElementById('addBallBtn');
        const ballCountDisplay = document.getElementById('ballCount');
        const controls = document.getElementById('controls');
        const infoPanel = document.getElementById('infoPanel');
        
        // 调整画布大小为全屏
        function resizeCanvas() {
            canvas.width = window.innerWidth;
            canvas.height = window.innerHeight;
        }
        
        // 初始化时调整大小
        resizeCanvas();
        window.addEventListener('resize', resizeCanvas);
        
        // 扩展颜色序列 (20种颜色)
        const colors = [
            '#FF0000', '#FF6600', '#FFFF00', '#00FF00', '#0000FF', 
            '#FF00FF', '#00FFFF', '#FF9900', '#99FF00', '#00FF99', 
            '#FF66CC', '#6633FF', 
        ];
        
        // 小球数组
        let balls = [];
        let ballSize = 30;
        let shrinkFactor = 10; // 缩小比例百分比
        let autoSpawnInterval;
        
        // 拖动信息面板功能
        function makeDraggable(element) {
            let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
            
            element.onmousedown = dragMouseDown;
            element.ontouchstart = dragTouchStart;
            
            function dragMouseDown(e) {
                e = e || window.event;
                e.preventDefault();
                pos3 = e.clientX;
                pos4 = e.clientY;
                document.onmouseup = closeDragElement;
                document.onmousemove = elementDrag;
            }
            
            function dragTouchStart(e) {
                const touch = e.touches[0];
                pos3 = touch.clientX;
                pos4 = touch.clientY;
                document.ontouchend = closeDragElement;
                document.ontouchmove = elementDrag;
                e.preventDefault();
            }
            
            function elementDrag(e) {
                e = e || window.event;
                e.preventDefault();
                
                // 计算新位置
                let clientX, clientY;
                if (e.type === 'mousemove') {
                    clientX = e.clientX;
                    clientY = e.clientY;
                } else {
                    clientX = e.touches[0].clientX;
                    clientY = e.touches[0].clientY;
                }
                
                pos1 = pos3 - clientX;
                pos2 = pos4 - clientY;
                pos3 = clientX;
                pos4 = clientY;
                
                // 设置元素新位置，确保不超出边界
                const newTop = element.offsetTop - pos2;
                const newLeft = element.offsetLeft - pos1;
                
                element.style.top = Math.max(0, Math.min(newTop, window.innerHeight - element.offsetHeight)) + "px";
                element.style.left = Math.max(0, Math.min(newLeft, window.innerWidth - element.offsetWidth)) + "px";
            }
            
            function closeDragElement() {
                // 停止移动
                document.onmouseup = null;
                document.onmousemove = null;
                document.ontouchend = null;
                document.ontouchmove = null;
            }
        }
        
        // 使信息面板可拖动
        makeDraggable(controls);
        
        // 更新小球大小显示
        ballSizeSlider.addEventListener('input', function() {
            ballSize = parseInt(this.value);
            ballSizeValue.textContent = ballSize;
        });
        
        // 更新缩小比例
        shrinkFactorSlider.addEventListener('input', function() {
            shrinkFactor = parseInt(this.value);
            shrinkFactorValue.textContent = shrinkFactor;
        });
        
        // 清除所有小球
        clearBtn.addEventListener('click', function() {
            balls = [];
            updateBallCount();
        });
        
        // 添加小球按钮
        addBallBtn.addEventListener('click', function() {
            spawnRandomBall();
        });
        
        // 自动生成小球
        autoSpawnToggle.addEventListener('change', function() {
            if (this.checked) {
                autoSpawnInterval = setInterval(spawnRandomBall, 1000);
            } else {
                clearInterval(autoSpawnInterval);
            }
        });
        
        // 在随机位置生成小球
        function spawnRandomBall() {
            const x = Math.random() * (canvas.width - ballSize * 2) + ballSize;
            const y = Math.random() * (canvas.height - ballSize * 2) + ballSize;
            const vx = (Math.random() - 0.5) * 5;
            const vy = (Math.random() - 0.5) * 5;
            
            balls.push({
                x: x,
                y: y,
                vx: vx,
                vy: vy,
                radius: ballSize,
                color: colors[Math.floor(Math.random() * colors.length)],
                spawnTime: Date.now()
            });
            
            updateBallCount();
        }
        
        // 点击生成小球
        canvas.addEventListener('click', function(e) {
            const rect = canvas.getBoundingClientRect();
            const x = e.clientX - rect.left;
            const y = e.clientY - rect.top;
            
            // 确保小球不会在边界生成
            const safeX = Math.max(ballSize, Math.min(x, canvas.width - ballSize));
            const safeY = Math.max(ballSize, Math.min(y, canvas.height - ballSize));
            
            const vx = (Math.random() - 0.5) * 5;
            const vy = (Math.random() - 0.5) * 5;
            
            balls.push({
                x: safeX,
                y: safeY,
                vx: vx,
                vy: vy,
                radius: ballSize,
                color: colors[Math.floor(Math.random() * colors.length)],
                spawnTime: Date.now()
            });
            
            updateBallCount();
        });
        
        // 触摸事件处理
        canvas.addEventListener('touchstart', function(e) {
            if (e.target === canvas) {
                const touch = e.touches[0];
                const mouseEvent = new MouseEvent('click', {
                    clientX: touch.clientX,
                    clientY: touch.clientY
                });
                canvas.dispatchEvent(mouseEvent);
                e.preventDefault();
            }
        }, { passive: false });
        
        // 更新小球数量显示
        function updateBallCount() {
            ballCountDisplay.textContent = balls.length;
        }
        
        // 检测碰撞
        function checkCollision(ball1, ball2) {
            const dx = ball2.x - ball1.x;
            const dy = ball2.y - ball1.y;
            const distance = Math.sqrt(dx * dx + dy * dy);
            
            return distance < ball1.radius + ball2.radius;
        }
        
        // 处理碰撞
        function handleCollision(ball1, ball2) {
            // 计算法线向量
            const nx = (ball2.x - ball1.x) / Math.sqrt(Math.pow(ball2.x - ball1.x, 2) + Math.pow(ball2.y - ball1.y, 2));
            const ny = (ball2.y - ball1.y) / Math.sqrt(Math.pow(ball2.x - ball1.x, 2) + Math.pow(ball2.y - ball1.y, 2));
            
            // 计算切向量
            const tx = -ny;
            const ty = nx;
            
            // 投影到切向和法向
            const v1n = nx * ball1.vx + ny * ball1.vy;
            const v1t = tx * ball1.vx + ty * ball1.vy;
            const v2n = nx * ball2.vx + ny * ball2.vy;
            const v2t = tx * ball2.vx + ty * ball2.vy;
            
            // 交换法向速度
            const v1nAfter = v2n;
            const v2nAfter = v1n;
            
            // 转换回x,y坐标
            ball1.vx = v1nAfter * nx + v1t * tx;
            ball1.vy = v1nAfter * ny + v1t * ty;
            ball2.vx = v2nAfter * nx + v2t * tx;
            ball2.vy = v2nAfter * ny + v2t * ty;
            
            // 如果缩小开关开启，则缩小小球
            if (shrinkToggle.checked) {
                shrinkBall(ball1);
                shrinkBall(ball2);
            }
        }
        
        // 缩小小球
        function shrinkBall(ball) {
            const shrinkAmount = ball.radius * (shrinkFactor / 100);
            ball.radius = Math.max(5, ball.radius - shrinkAmount); // 最小半径为5
            
            // 如果小球太小则移除
            if (ball.radius <= 5) {
                balls = balls.filter(b => b !== ball);
                updateBallCount();
            }
        }
        
        // 动画循环
        function animate() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            
            // 更新和绘制每个小球
            for (let i = 0; i < balls.length; i++) {
                const ball = balls[i];
                
                // 边界检测 - 碰到边界只反弹
                if (ball.x - ball.radius <= 0 || ball.x + ball.radius >= canvas.width) {
                    ball.vx = -ball.vx;
                    // 调整位置，防止卡在边界
                    ball.x = Math.max(ball.radius, Math.min(ball.x, canvas.width - ball.radius));
                }
                
                if (ball.y - ball.radius <= 0 || ball.y + ball.radius >= canvas.height) {
                    ball.vy = -ball.vy;
                    // 调整位置，防止卡在边界
                    ball.y = Math.max(ball.radius, Math.min(ball.y, canvas.height - ball.radius));
                }
                
                // 更新位置
                ball.x += ball.vx;
                ball.y += ball.vy;
                
                // 绘制小球
                ctx.beginPath();
                ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
                ctx.fillStyle = ball.color;
                ctx.fill();
                ctx.closePath();
            }
            
            // 碰撞检测
            if (collisionToggle.checked) {
                for (let i = 0; i < balls.length; i++) {
                    for (let j = i + 1; j < balls.length; j++) {
                        const ball1 = balls[i];
                        const ball2 = balls[j];
                        
                        // 检查小球是否已经存在超过1秒
                        const currentTime = Date.now();
                        if (currentTime - ball1.spawnTime > 1000 && currentTime - ball2.spawnTime > 1000) {
                            if (checkCollision(ball1, ball2)) {
                                handleCollision(ball1, ball2);
                            }
                        }
                    }
                }
            }
            
            requestAnimationFrame(animate);
        }
        
        // 启动动画
        animate();
    </script>
</body>
</html>